#ifndef _ENGINE_MATH_SSE_VEC4F_HPP
#define _ENGINE_MATH_SSE_VEC4F_HPP

#include "../../dllconfig.hpp"
#include <iostream>
#include "Vec3f.hpp"

namespace Engine 
{
	namespace Math 
	{
		namespace SSE
		{
			/// <summary>
			/// Descripes a four-dimensional (homogenous) vector and provides functions for working with them
			/// </summary>
			class _declspec(align(16)) _DLL_DECLSPEC Vec4f
			{
			public:
				/// <summary>
				/// Initializes a new instance of the Vec4f class
				/// </summary>
				Vec4f();

				Vec4f(float* _values);

				/*
				This is my first markup comment {label:comment1}
				*/

				/*{label:comment2} And this is a second one but with a " */

				/* This is not a markup comment, because label:comment3 is missing */

				"/*"
				/* this is more {label:comment5} tricky */

				"/* This is duplicated {label:comment2} */"

				" /* This is \" \\ not a comment " /*But this is {label:comment4}!!!
											 */

				/// <summary>
				/// Initializes a new instance of the Vec4f class
				/// </summary>
				/// <param name="_x">x-coordinate</param>
				/// <param name="_y">y-coordinate</param>
				/// <param name="_z">z-coordinate</param>
				Vec4f(float _x, float _y, float _z);
				/// <summary>
				/// Initializes a new instance of the Vec4f class
				/// </summary>
				/// <param name="_x">x-coordinate</param>
				/// <param name="_y">y-coordinate</param>
				/// <param name="_z">z-coordinate</param>
				/// <param name="_w">w-coordinate</param>
				Vec4f(float _x, float _y, float _z, float _w);
				/// <summary>
				/// Initializes a new instance of the Vec4 class
				/// </summary>
				/// <param name="_retToVec">Vector to perform a deep copy</param>
				Vec4f(const Vec4f &_refToVec);
				/// <summary>
				/// Initializes a new instance of the Vec4 class
				/// </summary>
				/// <param name="_retToVec">Vector to perform a deep copy</param>
				Vec4f(const Vec3f &_refToVec, float yW);
				/// <summary>
				/// Defines the assignment operator to get a deep copy
				/// </summary>
				/// <param name="_rhs">Source to copy from</param>
				/// <returns>Copied Vec4f object</returns>
				Vec4f& operator = (const Vec4f &_rhs);
				/// <summary>
				/// Releases the data from a Vec4 class
				/// </summary>
				~Vec4f();

				/// <summary>
				/// Sets or retrieves the value of the x-coordinate
				/// </summary>
				float X() const;
				/// <summary>
				/// Sets or retrieves the value of the x-coordinate
				/// </summary>
				/// <param name="_x">New value for the x-coordinate</param>
				/// <returns>A reference to the Vec4f class</returns>
				Vec4f& X(float _x);
				///<summary>
				///Sets or retrieves the value of the y-coordinate
				/// </summary>
				float Y(void) const;
				///<summary>
				///Sets or retrieves the value of the y-coordinate
				/// </summary>
				/// <param name="_y">New value for the y-coordinate</param>
				/// <returns>A reference to the Vec4f class</returns>
				Vec4f& Y(float _y);
				///<summary>
				///Sets or retrieves the value of the z-coordinate
				/// </summary>
				float Z(void) const;
				///<summary>
				///Sets or retrieves the value of the z-coordinate
				/// </summary>
				/// <param name="_z">New value for the z-coordinate</param>
				/// <returns>A reference to the Vec4f class</returns>
				Vec4f& Z(float _z);
				///<summary>
				///Sets or retrieves the value of the w-coordinate
				/// </summary>
				float W(void) const;
				///<summary>
				///Sets or retrieves the value of the w-coordinate
				/// </summary>
				/// <param name="_w">New value for the w-coordinate</param>
				/// <returns>A reference to the Vec4f class</returns>
				Vec4f& W(float _w);
				/// <summary>
				/// Returns a pointer to a OpenGL vector
				/// </summary>
				/// <returns>Pointer to OpenGL vector</returns>
				float* GetGlVector() const;

				Vec3f GetVec3f() const;

				///<summary>
				/// Projects the Vector to hyper plane w=1
				///</summary>
				Vec4f& Homogenize();

				static Vec4f Homogenize(const Vec4f& _vector);

				/// <summary>
				/// Normalizes the vector disregarding its last axis. 
				/// </summary>
				Vec4f& Normalize3();
				/// <summary>
				/// Normalizes a Vec4f object copy disregarding its last axis.
				/// </summary>
				/// <param name="_vector">A Vec4f to copy and normalize</param>
				/// <returns>The normalized vector</returns>
				static Vec4f Normalize3(const Vec4f &_vector);
				/// <summary>
				/// Normalizes the vector on all four axes. 
				/// </summary>
				Vec4f& Normalize();
				/// <summary>
				/// Normalizes a Vec4f object copy on all four axes.
				/// </summary>
				/// <param name="_vector">A Vec4f to copy and normalize</param>
				/// <returns>The normalized vector</returns>
				static Vec4f Normalize(const Vec4f &_vector);

				/// <summary>
				/// Returns the length of the vector disregarding its last axis
				/// </summary>
				float Length3() const;
				/// <summary>
				/// Returns the lenght of a Vec4f disregarding its last axis
				/// </summary>
				/// <param name="_vector">The vector</param>
				/// <returns>The lenght of the vector</returns>
				static float Length3(const Vec4f &_vector);
				/// <summary>
				/// Returns the length of the vector on all axes
				/// </summary>
				float Length() const;
				/// <summary>
				/// Returns the lenght of a Vec4f on all axes
				/// </summary>
				/// <param name="_vector">The vector</param>
				/// <returns>The length of the vector</returns>
				static float Length(const Vec4f &_vector);
			
				/// <summary>
				/// Calculates the dot product of two Vec4f objects disregarding their last axes
				/// </summary>
				/// <param name="_left">Left vector</param>
				/// <param name="_right">Right vector</param>
				/// <returns>The dot product of the two vectors</returns>
				static float Dot3(const Vec4f &_left, const Vec4f &_right);
				/// <summary>
				/// Calculates the cross product of two Vec4f objects disregarding their last axes
				/// </summary>
				/// <param name="_left">Left vector</param>
				/// <param name="_right">Right vector</param>
				/// <returns>The cross product of the two vectors</returns>
				static Vec4f Cross3(const Vec4f &_left, const Vec4f &_right);

				static Vec4f ComponentMin(const Vec4f& _v1, const Vec4f& _v2);
				static Vec4f ComponentMax(const Vec4f& _v1, const Vec4f& _v2);

				static Vec4f Interpolate(const Vec4f& _v1, const Vec4f& _v2, float _a1, float _a2);

				/// <summary>
				/// Defines a pointwise addition for Vec4f classes
				/// </summary>
				/// <param name="_rhs">Vector to add</param>
				/// <returns>The added Vector</returns>
				inline Vec4f operator + (const Vec4f &_rhs) const;
				/// <summary>
				/// Defines a pointwise addition for a Vec4f with a Vec3f classes (only first three components)
				/// </summary>
				/// <param name="_rhs">Vector to add</param>
				/// <returns>The added Vector</returns>
				inline Vec3f operator + (const Vec3f &_rhs) const;
				/// <summary>
				/// Subtracts to Vec4f classes pointwise
				/// </summary>
				/// <param name="_rhs">Vector to subtract</param>
				/// <returns>The Subtracted Vector</returns>
				inline Vec4f operator - (const Vec4f &_rhs) const;
				/// <summary>
				/// Subtracts a Vec3f from a Vec4f classes pointwise.
				/// </summary>
				/// <param name="_rhs">Vector to subtract</param>
				/// <returns>The Subtracted Vector</returns>
				inline Vec3f operator - (const Vec3f &_rhs) const;
				/// <summary>
				/// Multiplies the vector with a scalar
				/// </summary>
				/// <param name="_scalar">Scalar to multiply with</param>
				/// <returns>The multiplied vector</returns>
				inline Vec4f operator * (float _scalar) const;

				inline Vec4f operator / (float _scalar) const;

				/// <summary>
				/// Defines a pointwise addition for Vec4f classes
				/// </summary>
				/// <param name="_rhs">Vector to add</param>
				/// <returns>The added Vector</returns>
				inline Vec4f& operator += (const Vec4f &_rhs);

				inline Vec4f& operator += (const Vec3f &_rhs);

				/// <summary>
				/// Subtracts to Vec4f classes pointwise
				/// </summary>
				/// <param name="_rhs">Vector to subtract</param>
				/// <returns>The Subtracted Vector</returns>
				inline Vec4f& operator -= (const Vec4f &_rhs);

				inline Vec4f& operator -= (const Vec3f &_rhs);

				/// <summary>
				/// Multiplies the vector with a scalar
				/// </summary>
				/// <param name="_scalar">Scalar to multiply with</param>
				/// <returns>Multiplied vector</returns>
				inline Vec4f& operator *= (const float _scalar);

				inline Vec4f& operator /= (const float _scalar);

				/// <summary>
				/// Defines the equal-operator for two Vec4f objects
				/// </summary>
				/// <param name="_rhs">Vec4f class to check with</param>
				/// <returns>true if vectors are equal, false otherwise</returns>
				bool operator == (const Vec4f &_rhs) const;
				/// <summary>
				/// Defines the unequal-operator for two Vec4f objects
				/// </summary>
				/// <param name="_toCompare">Vec4f class to check with</param>
				/// <returns>true if vectors are unequal, false otherwise</returns>
				bool operator != (const Vec4f &_rhs) const;

			private:
				float values[4];
			};
		}
	}
}

_DLL_DECLSPEC std::ostream& operator <<(std::ostream &os, const Engine::Math::SSE::Vec4f &obj);

#endif

